package com.neusoft.modules.web.controller;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import com.neusoft.common.utils.FileInfoUtils;
import com.neusoft.common.utils.R;
import com.neusoft.common.utils.ServletUtils;
import com.neusoft.modules.web.entity.ChunkInfoEntity;
import com.neusoft.modules.web.entity.FileInfoEntity;
import com.neusoft.modules.web.entity.ao.MergeFileAO;
import com.neusoft.modules.web.entity.ao.UploadFileAO;
import com.neusoft.modules.web.entity.ao.UploadFileWithChunkAO;
import com.neusoft.modules.web.service.ChunkUploadService;
import com.neusoft.modules.web.service.FileUploadService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

/**
 * 实现图片、视频上传功能，支持分片上传功能，实现方式开放。
 */
@RestController
@RequestMapping("/file")
@Slf4j
public class FileUploadController {

    @Value("${upload.filePath}")
    private String uploadFolder;

    @Autowired
    private FileUploadService fileUploadService;

    @Autowired
    private ChunkUploadService chunkUploadService;

    /**
     * 上传整个文件
     * @param uploadFileAO
     * @return
     */
    @PostMapping("/upload")
    public R upload(UploadFileAO uploadFileAO){

        MultipartFile file = uploadFileAO.getUpfile();
        log.info("原名字: {}, 标识:{}, 文件名:{} ",
                file.getOriginalFilename(),  uploadFileAO.getIdentifier(), uploadFileAO.getFilename());

        //先判断整个文件是否已经上传过了
        FileInfoEntity temp = fileUploadService.queryFileInfoByIdentifier(
                uploadFileAO.getIdentifier(),uploadFileAO.getFilename());
        if(ObjectUtil.isNotNull(temp)) {
            return R.ok("文件已上传！");
        }

        boolean success = false;
        try {
            byte[] bytes = file.getBytes();
            //进行文件的合并操作
            String filename = uploadFileAO.getFilename();
            String filePath = uploadFolder + "/" + uploadFileAO.getIdentifier() + "/" + filename;

            Path path = Paths.get(filePath);
            Files.createDirectories(path.getParent());
            //文件写入指定路径
            Files.write(path, bytes);
            FileInfoEntity fileInfo = new FileInfoEntity();
            fileInfo.setId(IdUtil.simpleUUID());
            fileInfo.setFileName(filename);
            fileInfo.setIdentifier(uploadFileAO.getIdentifier());
            fileInfo.setLocation(filePath);
            fileInfo.setTotalSize(file.getSize());
            fileUploadService.saveFileInfo(fileInfo);
            success = true;
        } catch (IOException e) {
            e.printStackTrace();
            log.error(e.getMessage());
        }

        if(success) {
            return R.ok();
        } else {
            return R.error("文件上传失败！");
        }
    }

    /**
     * 分片上传
     * @param chunk
     * @return
     * @throws IOException
     */
    @PostMapping("/uploadWithChunk")
    public R uploadWithChunk(UploadFileWithChunkAO chunk) throws IOException {

        //先判断整个文件是否已经上传过了
        FileInfoEntity fileInfo = fileUploadService.queryFileInfoByIdentifier(chunk.getIdentifier(),chunk.getFilename());
        if(ObjectUtil.isNotNull(fileInfo)) {
            return R.ok("文件已上传！");
        }

        //如果完整文件不存在，则去数据库判断当前哪些文件块已经上传过了
        List<ChunkInfoEntity> list = chunkUploadService.queryChunkInfoByIdentifier(chunk.getIdentifier(),chunk.getFilename());
        boolean isUpload = false;
        for(ChunkInfoEntity chunkInfo : list) {
            if(chunk.getChunkNumber() == chunkInfo.getChunkNumber()) {
                // 文件块已上传
                isUpload = true;
                break;
            }
        }

        if(isUpload) {
            return R.ok("文件块已上传！");
        }

        MultipartFile file = chunk.getUpfile();
        log.info("原名字: {}, 块编号: {}, 标识:{}, 文件名:{} ",
                file.getOriginalFilename(), chunk.getChunkNumber(), chunk.getIdentifier(), chunk.getFilename());

        boolean success = false;
        try {
            byte[] bytes = file.getBytes();
            Path path = Paths.get(FileInfoUtils.generatePath(uploadFolder,
                    chunk.getIdentifier(), chunk.getFilename(), chunk.getChunkNumber()));
            //文件写入指定路径
            Files.write(path, bytes);
            ChunkInfoEntity chunkInfo = new ChunkInfoEntity();
            chunkInfo.setId(IdUtil.simpleUUID());
            chunkInfo.setChunkNumber(chunk.getChunkNumber());
            chunkInfo.setCurrentChunkSize(chunk.getCurrentChunkSize());
            chunkInfo.setIdentifier(chunk.getIdentifier());
            chunkInfo.setFileName(chunk.getFilename());
            chunkInfo.setTotalChunks(chunk.getTotalChunks());
            chunkInfo.setTotalSize(chunk.getTotalSize());
            chunkUploadService.saveChunkInfo(chunkInfo);
            success = true;
        } catch (IOException e) {
            e.printStackTrace();
            log.error(e.getMessage());
        }
        if(success) {
            return R.ok();
        } else {
            return R.error("文件上传失败！");
        }
    }

    /**
     * 合并文件
     * @param fileInfo
     * @return
     */
    @PostMapping("/mergeFile")
    public R mergeFile(@RequestBody UploadFileAO fileInfo){

        //先判断整个文件是否已经上传过了
        FileInfoEntity existFileInfo = fileUploadService.queryFileInfoByIdentifier(
                fileInfo.getIdentifier(),fileInfo.getFilename());
        if(ObjectUtil.isNotNull(existFileInfo)) {
            return R.ok("文件已合并！");
        }

        //前端组件参数转换为model对象
        FileInfoEntity fileInfoEntity = new FileInfoEntity();
        fileInfoEntity.setId(IdUtil.simpleUUID());
        fileInfoEntity.setFileName(fileInfo.getFilename());
        fileInfoEntity.setIdentifier(fileInfo.getIdentifier());

        //进行文件的合并操作
        String filename = fileInfo.getFilename();
        String file = uploadFolder + "/" + fileInfo.getIdentifier() + "/" + filename;
        String folder = uploadFolder + "/" + fileInfo.getIdentifier();
        String fileSuccess = FileInfoUtils.merge(file, folder, filename);

        fileInfoEntity.setLocation(file);

        //文件合并成功后，保存记录至数据库
        if("200".equals(fileSuccess)) {
            fileUploadService.saveFileInfo(fileInfoEntity);
            return R.ok();
        } else {
            return R.error();
        }
    }

    /**
     * 下载文件
     * @param req
     * @param resp
     */
    @GetMapping("/download")
    public void download(HttpServletRequest req, HttpServletResponse resp){
        String location = req.getParameter("location");
        String fileName = req.getParameter("filename");
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        OutputStream fos = null;
        try {
            bis = new BufferedInputStream(new FileInputStream(location));
            fos = resp.getOutputStream();
            bos = new BufferedOutputStream(fos);
            ServletUtils.setFileDownloadHeader(req, resp, fileName);
            int byteRead = 0;
            byte[] buffer = new byte[8192];
            while ((byteRead = bis.read(buffer, 0, 8192)) != -1) {
                bos.write(buffer, 0, byteRead);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                bos.flush();
                bis.close();
                fos.close();
                bos.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 根据标识和文件名查询文件信息
     * @param queryFileAO
     * @return
     */
    @PostMapping("/queryFileInfoByIdentifier")
    public R queryFileInfoByIdentifier(@RequestBody MergeFileAO queryFileAO){
        FileInfoEntity fileInfo = fileUploadService.queryFileInfoByIdentifier(
                queryFileAO.getIdentifier(),queryFileAO.getFilename());
        return R.ok().put("fileInfo", fileInfo);
    }



}
